package com.ljy.kotlindemo

import android.content.Context
import android.os.Build
import android.util.AttributeSet
import android.view.View
import androidx.annotation.RequiresApi
import com.example.ljy.kotlin001.Person
import com.squareup.moshi.Moshi
import org.jetbrains.annotations.TestOnly
import org.w3c.dom.Node
import java.io.File
import java.util.function.BinaryOperator
import java.util.function.IntBinaryOperator
import kotlin.properties.Delegates
import kotlin.reflect.KProperty

/**
 * @Author:         LiuJinYang
 * @CreateDate:     2020/7/21 13:46
 *
 * 类与对象
 */
class Test002 {
    //类成员: 类可以包含构造函数,初始化块,函数,属性,嵌套类,内部类,对象声明
    init {
        //创建类的实例: Kotlin并没有 new 关键字
        val dog11 = Dog1("dog1-1")
        val dog12 = Dog1("dog1-1")
        println(dog11.equals(dog12))
        println(dog11.toString())
        println(dog12.toString())
        val dog2 = Dog2()
        val dog3 = Dog3("dog3")
        val dog4 = Dog4("dog4")
        val dog5 = Dog5("dog5")
        val dog6 = Dog6("dog6")
        println(dog6.firstProperty)
        println(dog6.secondProperty)
        val dog7 = Dog7("dog7", "hhh", 4)
        println("dog7:${dog7.firstName}_${dog7.lastName}_${dog7.age}")
        val dog8 = Dog8(dog7)
        val dog9 = Dog9("dog9", dog8)
        val dog10 = Dog10(1)
//        val dog11 = Dog11()
        val cat1 = Cat1("cat1")
        cat1.sayHi()
        println(cat1.toString())
        val cat21 = Cat2("cat1")
        println(cat21.toString())
        val cat22 = Cat2("cat1", age = 4)
        println(cat22.toString())
        val cat3 = Cat3("cat3", "333")
        val cat4 = Cat4("cat3")
        cat4.Child().sayHi()
        val cat5 = Cat5(name = "cat5")
        cat5.sayHi()
        println(Cat6.id)
        Cat6.hello()

        val person21 = Person2(Gender.MAN)
        person21.name = "Liu"
        println(person21.name)
        println(person21.size)
        val person22 = Person2(Gender.WOMAN)
        person22.name = "Li"
        println(person22.name)
        println(person22.size)
    }
}

//在 Kotlin 中所有类都有⼀个共同的超类 Any，这对于没有超类型声明的类是默认超类
//默认情况下，Kotlin 类是最终（final）的：它们不能被继承。
// 要使⼀个类可继承，请⽤ open 关键字标记它
open class Dog(val name: String) {
    //Any 有三个⽅法：equals() 、 hashCode() 与 toString()
    override fun equals(other: Any?): Boolean {
        return if (other is Dog) name.equals(other.name)
        else super.equals(other)
    }

    override fun hashCode(): Int {
        return super.hashCode()
    }

    override fun toString(): String {
        return "Dog(name='$name')"
    }

}

//类: Kotlin 中使⽤关键字 class 声明类
//如需声明⼀个显式的超类型，请在类头中把超类型放到冒号之后
class Dog1(name: String) : Dog(name) {
}

// 如果⼀个类没有类体，可以省略花括号
class Dog2

//构造函数:
//Kotlin 中的⼀个类可以有⼀个主构造函数以及⼀个或多个次构造函数。

// 主构造函数是类头的⼀部分：它跟在类名（与可选的类型参数）后
class Dog3 constructor(name: String) {

}

//如果主构造函数没有任何注解或者可⻅性修饰符，可以省略这个 constructor 关键字
class Dog4(name: String) {

}

//如果构造函数有注解或可⻅性修饰符，这个 constructor 关键字是必需的，并且这些修饰符在它前⾯
class Dog5 public @TestOnly constructor(name: String) {

}

//主构造函数不能包含任何的代码。
// 初始化的代码可以放到以 init 关键字作为前缀的初始化块（initializer blocks）中
//在实例初始化期间，初始化块按照它们出现在类体中的顺序执⾏，与属性初始化器交织在⼀起：
class Dog6(name: String) {
    val firstProperty = "First property:$name".also(::println)

    init {
        println("First initializer block that prints ${name}")
    }

    val secondProperty = "Second property: ${name.length}".also(::println)

    init {
        println("Second initializer block that prints ${name.length}")
    }
}

//声明属性以及从主构造函数初始化属性，Kotlin 有简洁的语法：
//主构造函数中声明的属性可以是可变的（var）或只读的（val）
class Dog7(val firstName: String, val lastName: String, var age: Int) {
    var children: MutableList<Dog8> = mutableListOf()
}

//次构造函数: 类也可以声明前缀有 constructor的次构造函数
class Dog8 {
    var children: MutableList<Dog9> = mutableListOf()

    constructor(dog: Dog7) {
        dog.children.add(this)
    }
}

//如果类有⼀个主构造函数，每个次构造函数需要委托给主构造函数，
// 可以直接委托或者通过别的次构造函数间接委托。
// 委托到同⼀个类的另⼀个构造函数⽤ this 关键字即可
class Dog9(val name: String) {

    constructor(name: String, parent: Dog8) : this(name) {
        parent.children.add(this)
    }
}

//初始化块中的代码实际上会成为主构造函数的⼀部分。委托给主构造函数会作为次构造函数的第⼀条语句，
// 因此所有初始化块与属性初始化器中的代码都会在次构造函数体之前执⾏。
// 即使该类没有主构造函数，这种委托仍会隐 式发⽣，并且仍会执⾏初始化块：
class Dog10 {
    init {
        println("init block")
    }

    constructor(i: Int) {
        println("constructor")
    }
}

//如果⼀个⾮抽象类没有声明任何（主或次）构造函数，它会有⼀个⽣成的不带参数的主构造函数。
// 构造函数的可⻅性是public。如果你不希望你的类有⼀个公有构造函数，你需要声明⼀个带有⾮默认可⻅性的空的主构造函数
class Dog11 private constructor() {

}

//如果派⽣类没有主构造函数，那么每个次构造函数必须使⽤ super 关键字初始化其基类型，或委托给另⼀个构造函数
//做到这⼀点。 注意，在这种情况下，不同的次构造函数可以调⽤基类型的不同的构造函数
class MyView : View {
    constructor(context: Context) : super(context)
    constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet)
}

open class Cat(val name: String) {
    open fun sayHi() {
        println("$name say: hi~~")
    }

    fun hello() {
        println("$name hello~~")
    }

    override fun toString(): String {
        return "Cat(name='$name', age=$age)"
    }

    init {
        println("Initializing Cat")
    }

    open val size: Int = name.length.also {
        println("Initializing size in Cat:$it")
    }
    open val age: Int = 0
}

class Cat1(name: String) : Cat(name) {
    //覆盖方法
    //Kotlin ⼒求清晰显式。因此，Kotlin 对于可覆盖的成员（我们称之为 开放 ）
    // 以及覆盖后的成员需要显式修饰符
    //覆盖方法必须加上 override 修饰符
    override fun sayHi() {
        println("say hi start")
        super.sayHi()
        println("say hi end")
    }

    //父类的hello方法没有open，不论加不加 override⼦类中都不允许定义相同签名的函数
//    fun hello(){
//        println("$name hello~~")
//    }

    //覆盖属性
    //属性覆盖与⽅法覆盖类似；在超类中声明然后在派⽣类中重新声明的属性必须以 override 开头，、
    // 并且它们必须具有兼容的类型
    //    override val age: Int = 1
    //可以⽤⼀个 var 属性覆盖⼀个 val 属性，但反之则不⾏
    //因为⼀个 val 属性本质上声明了⼀个 get ⽅法，
    // ⽽将其覆盖为 var 只是在⼦类中额外声明⼀个 set ⽅法
    override var age: Int = 1
}

//可以在主构造函数中使⽤ override 关键字作为属性声明的⼀部分
class Cat2(name: String, override val age: Int = 2) : Cat(name)

//派⽣类初始化顺序
//基类构造函数执⾏时，派⽣类中声明或覆盖的属性都还没有初始化。如果在基类初始化逻辑中（直接或通过
//另⼀个覆盖的 open 成员的实现间接）使⽤了任何⼀个这种属性，那么都可能导致不正确的⾏为或运⾏时故障。设计⼀
//个基类时，应该避免在构造函数、属性初始化器以及 init 块中使⽤ open 成员
class Cat3(name: String, val lastName: String) :
    Cat(name.capitalize().also { println("Argument for Cat:$it") }) {
    init {
        println("Initializing Cat3")
    }

    override val size: Int =
        (super.size + lastName.length).also { println("Initializing size in Cat3: $it") }
}

//调⽤超类实现
//派⽣类中的代码可以使⽤ super 关键字调⽤其超类的函数与属性访问器的实现
class Cat4(name: String) : Cat(name) {
    override fun sayHi() {
        println("say hi start")
        super.sayHi()
        println("say hi end")
    }

    val info: String get() = "info=${super.name}_${super.age}"

    //在⼀个内部类中访问外部类的超类，可以通过由外部类名限定的 super 关键字来实现：super@Outer
    inner class Child {
        fun sayHi() {
            super@Cat4.sayHi()
        }
    }
}

//覆盖规则：
// Kotlin 中，实现继承由下述规则规定：如果⼀个类从它的直接超类继承相同成员的多个实现， 它必须覆盖这个成员
//并提供其⾃⼰的实现（也许⽤继承来的其中之⼀）。 为了表⽰采⽤从哪个超类型继承的实现，我们使⽤由尖括号中超类
//型名限定的 super
interface Hi {
    fun sayHi() {
        println("Hi.sayHi()")
    }
}

class Cat5(name: String) : Cat(name), Hi {
    override fun sayHi() {
        super<Cat>.sayHi()
        super<Hi>.sayHi()
    }
}

//抽象类
//类以及其中的某些成员可以声明为 abstract。 抽象成员在本类中可以不⽤实现。
//并不需要⽤open 标注⼀个抽象类或者函数⸺因为这不⾔⽽喻
//可以⽤⼀个抽象成员覆盖⼀个⾮抽象的开放成员
abstract class CatAbs(name: String) : Cat(name) {
    abstract override fun sayHi()
}

//伴生对象
//Kotlin 取消了关键字static，也就无法直接声明静态成员，所以引入了伴生对象这个概念
//如果你需要写⼀个可以⽆需⽤⼀个类的实例来调⽤、但需要访问类内部的函数（例如，⼯⼚⽅法），
// 你可以把它写成该类内对象声明中的⼀员
//更具体地讲，如果在你的类内声明了⼀个伴⽣对象， 你就可以访问其成员，只是以类名作为限定符
class Cat6(name: String) : Cat(name) {
    companion object Obj {
        const val id: String = "006"
        fun hello() {
            println("hello")
        }
    }
}

//属性： 用var/val声明， “对象名.属性名”引用
class Person1 {
    //可以自定义setter/getter
    var name: String
        get() = "name=$name"
        set(value) {
            name = "$value~~"
        }

    //如果可以从 getter 推断出属性类型，则可以省略
    val size get() = this.name.length
}

enum class Gender {
    MAN,
    WOMAN
}

class Person2(val gender: Gender) {
    //幕后字段
    //当⼀个属性需要⼀个幕后字段时，Kotlin 会⾃动提供。
    // 这个幕后字段可以使⽤ field 标识符在访问器中引⽤（类似于this代表当前类）
    var name: String = ""
        set(value) {
            field = when (gender) {
                Gender.MAN -> "Mr.$value"
                Gender.WOMAN -> "Miss.$value"
            }
            _size = field.length
        }

    //幕后属性
    //对外表现为只读，对内表现为可读可写
    var _size: Int? = null
    val size: Int
        get() {
            return _size ?: 0
        }
}

//编译期常量
//如果只读属性的值在编译期是已知的，那么可以使⽤ const 修饰符将其标记为 编译期常量
//以 String 或原⽣类型值初始化, 没有⾃定义 getter
//可以⽤在注解中：
const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"

@Deprecated(SUBSYSTEM_DEPRECATED)
fun foo() {
}

//延迟初始化属性与变量
//⼀般地，属性声明为⾮空类型必须在构造函数中初始化。 然⽽，这经常不⽅便。例如：属性可以通过依赖注⼊来初始化，
//或者在单元测试的 setup ⽅法中初始化。 这种情况下，你不能在构造函数内提供⼀个⾮空初始器。 但你仍然想在类体
//中引⽤该属性时避免空检测。
//为处理这种情况，你可以⽤ lateinit 修饰符标记该属性：
public class MyTest {
    lateinit var person: Person

    fun setUp() {
        person = Person("", "")
    }


}

//要检测⼀个 lateinit var 是否已经初始化过，请在该属性的引⽤上使⽤ .isInitialized ：
public class CCC {
    fun isInit() {
        var myTest = MyTest()
        if (myTest::person.isLateinit) {
            println(myTest.person.name)
        }
    }
}

//接口
interface MyInterface {
    //Kotlin 的接⼝可以既包含抽象⽅法的声明也包含实现。与抽象类不同的是，接⼝⽆法保存状态。
    fun bar()
    fun foo() {
        //可选的方法体
        print(prop)
    }

    // 它可以有属性但必须声明为抽象或提供访问器实现
// 在接⼝中声明的属性要么是抽象的，要么提供访问器的实现。在接⼝中声明的属性不能有幕
// 后字段（backing field），因此接⼝中声明的访问器不能引⽤它们
    val prop: Int
    val propertyWithImplementation: String get() = "foo"

}

//实现接口
class Child(override val prop: Int) : MyInterface {
    override fun bar() {
        TODO("Not yet implemented")
    }
}

//接口继承
interface Named {
    val name: String
}

interface People : Named {
    val firstName: String
    val lastName: String

    override val name: String
        get() = "$firstName $lastName"
}

data class Employee(
    //不必实现name
    override val firstName: String,
    override val lastName: String,
    val email: String
) : People

//解决覆盖冲突
//实现多个接⼝时，可能会遇到同⼀⽅法继承多个实现的问题
interface A {
    fun foo() {
        print("A")
    }

    fun bar()
}

interface B {
    fun foo() {
        print("B")
    }

    fun bar() {
        print("bar")
    }
}

class C : A {
    override fun bar() {
        println("Bar")
    }
}

class D : A, B {
    override fun foo() {
        super<A>.foo()
        super<B>.foo()
    }

    override fun bar() {
        super<B>.bar()
    }
}

//可见性修饰符
//Kotlin 中有这四个可⻅性修饰符：private 、 protected 、 internal 和 public

//对于包内声明：
// 函数、属性和类、对象和接⼝可以在顶层声明，即直接在包内
//如果没有显式指定修饰符的话，默认可⻅性是 public，意味着你的声明将随处可⻅
//如果你声明为 private，它只会在声明它的⽂件内可⻅
//如果你声明为 internal，它会在相同模块内随处可⻅
//protected 不适⽤于顶层声明
//对于类内部声明的成员：
//private 意味着只在这个类内部（包含其所有成员）可⻅
//protected ⸺ 和 private ⼀样 + 在⼦类中可⻅
//internal ⸺ 能⻅到类声明的  本模块内 的任何客⼾端都可⻅其 internal 成员
//public ⸺ 能⻅到类声明的任何客⼾端都可⻅其 public 成员
//注意在 Kotlin 中，外部类不能访问内部类的 private 成员
//如果你覆盖⼀个 protected 成员并且没有显式指定其可⻅性，该成员还会是 protected 可⻅性
open class Outer {
    private val a = 1
    protected open val b = 2
    internal val c = 3
    public val d = 4

    protected class Nested {
        public val e = 5
    }
}

class Subclass : Outer() {
    //    val n1 = super.a
    val n2 = super.b
    val n3 = super.c
    val n4 = super.d
    protected val n5 = Nested()
    val n6 = Nested().e
}

class Unrelated(o: Outer) {
    //    val n1=o.a
//    val n2=o.b
    val n3 = o.c
    val n4 = o.d
//    val n5=o.Nested()
//    val n6=o.Nested().e
}

//构造函数可见性
//需要添加⼀个显式 constructor 关键字
class E private constructor(val e: Int) {
    fun foo() {
        print("E:$e")
    }
}

//局部声明： 局部变量，函数和类不能有可见性修饰符

//模块
//可⻅性修饰符 internal 意味着该成员只在相同模块内可⻅。
// 更具体地说，⼀个模块是编译在⼀起的⼀套 Kotlin ⽂ 件：
//⼀个 IntelliJ IDEA 模块；
//⼀个 Maven 项⽬；
//⼀个 Gradle 源集（例外是 test 源集可以访问 main 的 internal 声明）；
//⼀次 <kotlinc> Ant 任务执⾏所编译的⼀套⽂件

//扩展：
//你可以为⼀个你不能修改的、来⾃第三⽅库中的类编写⼀个新的函数。 这个新增的函数就像那个原始类本来
//就有的函数⼀样，可以⽤普通的⽅法调⽤。 这种机制称为  扩展函数 。
// 此外，也有  扩展属性 ， 允许你为⼀个已经存在的类添加新的属性。
//声明⼀个扩展函数，我们需要⽤⼀个  接收者类型 也就是被扩展的类型来作为他的前缀
fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
    val tmp = this[index1]//this对应该列表
    this[index1] = this[index2]
    this[index2] = tmp
}

fun printSwap() {
    val list = mutableListOf(1, 2, 3, 4, 5)
    list.swap(0, 1)
    println(list.toString())
}

//扩展是静态解析的
//调⽤的扩展函数是由函数调⽤所在的表达式的类型来决定的， ⽽不是由表达式运⾏时求值结果决定的
open class Shape {
    open fun foo() = "Shape.foo()"
}

class Rectangle : Shape() {
    override fun foo() = "Rectangle.foo()"
}

fun Shape.getName() = "Shape.getName()"

fun Rectangle.getName() = "Rectangle.getName()"

fun printInfo(s: Shape) {
    println(s.foo())
    println(s.getName())
}

//如果⼀个类定义有⼀个成员函数与⼀个扩展函数，⽽这两个函数⼜有相同的接收者类型、 相同的名字，
// 并且都适⽤给定的参数，这种情况总是取成员函数
class Example {
    fun printFunType() {
        println("class fun")
    }

    override fun toString(): String {
        return "Example.toString()"
    }
}

fun Example.printFunType() {
    println("Extension fun")
}

fun Example.printFunType(i: Int) {
    println("Extension fun , param:$i")
}

//可空接收者
fun Any?.toString(): String {
    if (this == null) return "is null..."
    return toString()
}

//扩展属性
val <T> List<T>.lastIndex: Int
    get() = size - 1
//由于扩展没有实际的将成员插⼊类中，因此对扩展属性来说幕后字段是⽆效的。
// 这就是为什么扩展属性不能有初始化器。他们的⾏为只能由显式提供的 getters/setters 定义
//val <T> List<T>.firstIndex: Int=0 //错误：扩展属性不能有初始化器

//伴生对象的扩展
//如果⼀个类定义有⼀个伴⽣对象 ，你也可以为伴⽣对象定义扩展函数与属性。
// 就像伴⽣对象的常规成员⼀样， 可以只使⽤类名作为限定符来调⽤伴⽣对象的扩展成员
class MyClass {
    companion object {}
}

fun MyClass.Companion.printCompanion() {
    println("printCompanion")
}

//扩展的作用域
//1. ⼤多数时候我们在顶层定义扩展⸺直接在包⾥
//2. 要使⽤所定义包之外的⼀个扩展，我们需要在调⽤⽅导⼊它

//扩展声明为成员
//在⼀个类内部你可以为另⼀个类声明扩展。在这样的扩展内部，有多个  隐式接收者 ⸺ 其中的对象成员可以⽆需通过
//限定符访问。扩展声明所在的类的实例称为  分发接收者 ，扩展⽅法调⽤所在的接收者类型的实例称为  扩展接收者
class Host(val hostname: String) {
    fun printHostname() {
        print(hostname)
    }
}

class Connection(val host: Host, val port: Int) {
    fun printPort() {
        println(port)
    }

    fun Host.printConnectionString() {
        printHostname()
        print(":")
        printPort()
        //对于分发接收者与扩展接收者的成员名字冲突的情况，扩展接收者优先
        println(toString())//Host.toStirng()
        println(this.toString())//Host.toString()
        println(this@Connection.toString())//Connection.toString()
    }

    fun connect() {
        host.printConnectionString()
    }
}

//声明为成员的扩展可以声明为 open 并在⼦类中覆盖
//这意味着这些函数的分发对于分发接收者类型是虚拟的，但对于扩展接收者类型是静态的
open class Base() {}

class Derived : Base() {}

open class BaseCaller {
    open fun Base.printFunctionInfo() {
        println("BaseCaller Base.printFunctionInfo")
    }

    open fun Derived.printFunctionInfo() {
        println("BaseCaller Derived.printFunctionInfo")
    }

    fun call(b: Base) {
        b.printFunctionInfo()
    }
}

class DerivedCaller : BaseCaller() {
    override fun Base.printFunctionInfo() {
        println("DerivedCaller Base.printFunctionInfo")
    }

    override fun Derived.printFunctionInfo() {
        println("DerivedCaller Derived.printFunctionInfo")
    }
}

//数据类
//我们经常创建⼀些只保存数据的类。 在这些类中，⼀些标准函数往往是从数据机械推导⽽来的。
// 在 Kotlin 中，这叫做  数据类 并标记为 data ：
data class User(val name: String, val age: Int)
//编译器⾃动从主构造函数中声明的所有属性导出以下成员:
//1 equals() / hashCode() 对
//2 toString() 格式是 "User(name=John, age=42)"
//3 componentN() 函数 按声明顺序对应于所有属性
//4 copy() 函数
//为了确保⽣成的代码的⼀致性以及有意义的⾏为，数据类必须满⾜以下要求
//1. 主构造函数需要⾄少有⼀个参数；
//2. 主构造函数的所有参数需要标记为 val 或 var ；
//3. 数据类不能是抽象、开放、密封或者内部的；
//4. （在1.1之前）数据类只能实现接⼝。

//在 JVM 中，如果⽣成的类需要含有⼀个⽆参的构造函数，则所有的属性必须指定默认值。
data class User2(val name: String = "", val age: Int = 0)

//在类体中声明的属性
//那些⾃动⽣成的函数，编译器只使⽤在主构造函数内部定义的属性。
// 如需在⽣成的实现中排除⼀个属性，请将其声明在类体中
data class People2(val name: String) {
    var age: Int = -1
}

//复制
//复制的同时 可以改变某些属性
//fun copy(name: String = this.name, age: Int = this.age) = User(name, age)

//标准数据类
//标准库提供了 Pair 与 Triple 。尽管在很多情况下具名数据类是更好的设计选择，
// 因为它们通过为属性提供有意义的名称使代码更具可读性。

//密封类
//⽤来表⽰受限的类继承结构：当⼀个值为有限⼏种的类型、⽽不能有任何其他类型时。在某种意义上，他们是枚
//举类的扩展：枚举类型的值集合也是受限的，但每个枚举常量只存在⼀个实例，⽽密封类的⼀个⼦类可以有可包含状态 的多个实例
//需要在类名前⾯添加 sealed 修饰符
//虽然密封类也可以有⼦类，但是所有⼦类都必须在与密封类⾃⾝相同的⽂件中声明
//⼀个密封类是⾃⾝抽象的，它不能直接实例化并可以有抽象（abstract）成员。
//密封类不允许有⾮-private 构造函数（其构造函数默认为 private）。
//注意，扩展密封类⼦类的类（间接继承者）可以放在任何位置，⽽⽆需在同⼀个⽂件中。
sealed class Exor
data class Const(val number: Double) : Exor()
data class Sum(val e1: Exor, val e2: Exor) : Exor()
object NotANumber : Exor()

//泛型
class Box<T>(t: T) {
    var value = t
}

val box: Box<Int> = Box<Int>(9)//泛型"> ="之间的空格是必须的

//如果类型参数可以推断出来，例如从构造函数的参数或者从其他途径，允许省略类型参数：
val box2 = Box(18)


//委托
//Kotlin 之所以提供委托这个语法，主要是为了方便我们使用者，让我们可以很方便的实现代理这样的模式。
// 这一点在 Kotlin 的委托属性这一特性上体现得更是淋漓尽致。
interface Animal {
    fun bark()
}

class CatA : Animal {
    override fun bark() {
        println("mi mi ~~")
    }
}

class Zoo(animal: Animal) : Animal by animal

//by lazy 实现懒加载
private fun compute(): String {
    return "hello"
}

val lazyValue: String by lazy {
    val result = compute()
    println("computed")
    result
}

//by Delegates.observable 实现观察者模式的变量
class UserA {
    var name: String by Delegates.observable("<no name>") { property, oldValue, newValue ->
        println("name 改变了:$oldValue -> $newValue")
    }
}

class OuterA {
    private val bar: Int = 1

    //嵌套类
    class Nested {
        fun foo() = 2
    }

    //内部类
    //标记为inner的嵌套类能够访问其外部类的成员，内部类会带有一个外部类的对象的引用
    inner class InnerA {
        private val bar: Int = 3
        fun foo() = this@OuterA.bar
        fun foo2() = bar
    }

}

val demo1 = OuterA.Nested().foo()
val demo2 = OuterA().InnerA().foo()
val demo3 = OuterA().InnerA().foo2()

//匿名内部类

//枚举类
//实现类型安全的枚举
enum class Direction {
    NORTH, SOUTH, WEST, EAST,
}

//每个枚举常量都是一个枚举类的实例对象，所以他们是可以如下这样初始化的
enum class Color(val rgb: Int) {
    RED(0xff0000),
    GREEN(0x00ff00),
    BLUE(0x0000ff),
}

//匿名类
//枚举常量还可以声明其带有相应方法及覆盖了其基类方法的匿名类
enum class ProtocolState {
    WAITING {
        override fun signal(): ProtocolState {
            return TALKING
        }
    },
    TALKING {
        override fun signal() = WAITING
    };
    //用：分号将枚举常量和其他成员（变量函数等）分开

    abstract fun signal(): ProtocolState
}

//在枚举类中实现接口
@RequiresApi(Build.VERSION_CODES.N)
enum class IntArithmetics : BinaryOperator<Int>, IntBinaryOperator {
    PLUS {
        override fun apply(t: Int, u: Int): Int {
            return t + u
        }
    },
    TIMES {
        override fun apply(t: Int, u: Int): Int {
            return t * u
        }
    };

    override fun applyAsInt(t: Int, u: Int): Int {
        return apply(t, u)
    }

}

//使用枚举常量
enum class RGB {
    RED, GREEN, BLUE
}

inline fun <reified T : Enum<T>> printAllValues() {
    println(enumValues<T>().joinToString { it.name })
}

//对象表达式与对象声明
//类似java的匿名内部类
//如果超类型有一个构造函数,则必须传递适当的构造函数参数给它,多个超类型可以由跟在冒号后面的逗号分隔的列表指定
open class A1(x: Int) {
    public open val y: Int = x
}

interface B1 {

}

val ab: A1 = object : A1(1), B1 {
    override val y: Int
        get() = 12
}

//任何时候，如果我们只需要“⼀个对象⽽已”，并不需要特殊超类型，那么我们可以简单地写
private fun foo1() {
    val ab2 = object {
        var x: Int = 0
        var y: Int = 0
    }

    println(ab2.x + ab2.y)
}

class C1 {
    //私有函数,所以其返回类型是匿名对象类型
    private fun foo() = object {
        val x: String = "X"
    }

    //公有函数,所以其返回类型是any
    fun foo2() = object {
        val x: String = "X2"
    }

    fun bar() {
        val x1: Any = foo()
        val x2 = foo2()
        println(x1)
        println(x2)
        val x3: Any = foo().x
//        val x4 =foo2().x
    }

}

//对象表达式中的代码可以访问来⾃包含它的作⽤域的变量
open class MouseAdapter {
    open fun mouseClicked() {

    }

    open fun mouseEntered() {

    }
}

class JComponent {
    fun addMouseListener(adapter: MouseAdapter) {

    }
}

private fun countCLicks(window: JComponent) {
    var clickCount = 0
    var enterCount = 0
    window.addMouseListener(object : MouseAdapter() {
        override fun mouseClicked() {
            clickCount++
        }

        override fun mouseEntered() {
            enterCount++
        }
    })
}

//对象声明
//单例是一个很有用的模式，Kotlin声明单例很简单
object Data1 {
    fun register() {
        data = "def"
    }

    var data = "abc"
}

private fun useData() {
    println(Data1.data)
    Data1.register()
    println(Data1.data)
    Data1.data = "qwer"
    println(Data1.data)

}

//伴生对象
class MyClass1 {
    companion object Factory {
        fun create(): MyClass1 = MyClass1()
    }
}

class MyClass2 {
    //伴生对象名可以忽略
    companion object {
        fun create(): MyClass2 = MyClass2()
    }
}

//即使伴生对象成员看起来像其他语言中的静态成员，在运行时这些仍然是真正对象的实例成员，并且可以实现接口
interface Factory<T> {
    fun create(): T
}

class MyClass3 {
    companion object : Factory<MyClass3> {
        override fun create(): MyClass3 = MyClass3()
    }
}

private fun useCompanion() {
    val instance1 = MyClass1.Factory.create()
    val instance2 = MyClass2.Companion.create()
    val instance3 = MyClass3.Companion.create()
}

//对象表达式和对象声明之间有⼀个重要的语义差别：
//对象表达式是在使⽤他们的地⽅⽴即执⾏（及初始化）的；
//对象声明是在第⼀次被访问到时延迟初始化的；
//伴⽣对象的初始化是在相应的类被加载（解析）时，与 Java 静态初始化器的语义相匹配。

//类型别名
//类型别名为现有类型提供替代名称,有助于缩短较⻓的泛型类型|
//1. 缩减集合类型
typealias NodeSet = Set<Node>
typealias FileTable<K> = MutableMap<K, MutableList<File>>
//2. 为函数类型提供别名
typealias MyHandler = (Int, String, Any) -> Unit
typealias Predicate<T> = (T) -> Boolean

//为内部类和嵌套类创建新名称
class AA {
    inner class InnerA
}

typealias AAInner = AA.InnerA

val aai: AAInner = AA().InnerA()

//内联类
//通过在类的前⾯定义⼀个 inline 修饰符来声明
//本质: 把函数体复制粘贴到函数调用处
//内联类必须含有唯⼀的⼀个属性在主构造函数中初始化。在运⾏时，将使⽤这个唯⼀属性来表⽰内联类的实例
//内联类允许去继承接⼝
interface Printable {
    fun print(): String
}

inline class PassWord(val value: String) : Printable {
    //内联类⽀持普通类中的⼀些功能。特别是，内联类可以声明属性与函数
    val length: Int get() = value.length
    fun logInfo() {
        println("password=$value")
    }

    override fun print(): String {
        return "hi, $value"
    }

    //内联类不能含有 init 代码块
    //内联类不能含有幕后字段, 因此，内联类只能含有简单的计算属性（不能含有延迟初始化/委托属性）
}

val pwd = PassWord("123456")

//表示方式
//Kotlin 编译器为每个内联类保留⼀个包装器。内联类的实例可以在运⾏时表⽰为包装器或者基础类型。
// 这就类似于 Int 可以表⽰为原⽣类型 int 或者包装器 Integer 。
//为了⽣成性能最优的代码，Kotlin 编译更倾向于使⽤基础类型⽽不是包装器.
//然⽽，有时候使⽤包装器是必要的。⼀般来说，只要将内联类⽤作另⼀种类型，它们就会被装箱.
interface Interf
inline class Foo(val i: Int) : Interf

fun asInline(f: Foo) {}
fun <T> asGeneric(x: T) {}
fun asInterface(i: Interf) {}
fun asNullable(i: Foo?) {}
fun <T> id(x: T): T = x

private fun testInterf() {
    val f = Foo(38)
    asInline(f)//拆箱,用作Foo
    asGeneric(f)//装箱,用作泛型T
    asInterface(f)//装箱, 用作类型Interf
    asNullable(f)//装箱,用作不高于Foo的可空类型Foo?
    //在下⾯这⾥例⼦中，'f' ⾸先会被装箱（当它作为参数传递给 'id' 函数时）然后⼜被拆箱（当它从'id'函数中被返回时）
    //最后， 'c' 中就包含了被拆箱后的内部表达(也就是 '38')， 和 'f' ⼀样
    val c = id(f)
}
//因为内联类既可以表⽰为基础类型有可以表⽰为包装器，引⽤相等对于内联类⽽⾔毫⽆意义，因此这也是被禁⽌的

//委托
//委托模式已经证明是实现继承的⼀个很好的替代⽅式， ⽽ Kotlin 可以零样板代码地原⽣⽀持它
interface Base1{
    fun print()
    val msg:String
}

class BaseImpl(val x:Int):Base1{
    override fun print() {
        println(msg)
    }
    override val msg: String="BaseImpl.msg=$x"
}


class Derived1(b:Base1):Base1 by b{
    //覆盖由委托实现的接⼝成员
    //编译器会使⽤ override 覆盖的实现⽽不是委托对象中的
//    override fun print() {
//        println("abc")
//    }
    //但请注意，以这种⽅式重写的成员不会在委托对象的成员中调⽤ ，委托对象的成员只能访问其⾃⾝对接⼝成员实现：
    // 在 b 的 `print` 实现中不会访问到这个属性
    override val msg: String="Derived1.msg"

}

private fun testBy(){
    val b =BaseImpl(10)
    val d=Derived1(b)
    d.print()
    println(d.msg)

}


//委托属性
//一些常见属性，不想每次手动实现，想只实现一次并放入一个库
class Delegate{
    operator fun getValue(thisRef:Any?,property:KProperty<*>):String{
        return "$thisRef, thank you for delegating ’${property.name}‘ to me"
    }

    operator fun setValue(thisRef: Any?,property: KProperty<*>,value: String){
        println("$value has been assigned to '${property.name}' in $thisRef")
    }
}
class Example1{
    var msg:String by Delegate()
}

private fun testDelegate(){
    val e=Example1()
    println(e.msg)
    e.msg="newValue"
    println(e.msg)
}
//Kotlin 标准库为⼏种有⽤的委托提供了⼯⼚⽅法
//1. 延迟属性 Lazy
val  lazy1:String by lazy {
    println("init lazy")
    "Hello"
}
private fun testLazy(){
    println(lazy1)
    println(lazy1)
    println(lazy1)
}
//2. 可观察属性 Observable
class User1{
    var name:String by Delegates.observable("<no name>"){
        property, oldValue, newValue -> println("$oldValue -> $newValue")
    }
}
private fun testObservable(){
    val u=User1()
    u.name="abc"
    u.name="def"
}
//3. 把属性储存在映射中
class User22(val map:Map<String,Any?>){
    val name:String by map
    val age:Int by map
}

private fun testMap1(){
    val user=User22(mapOf(
        "age" to 12,
        "name" to "bob"
    ))
    println(user.name)
    println(user.age)
}

fun main() {
    Test002()

    val s = Shape()
    val r = Rectangle()
    printInfo(s)
    printInfo(r)

    Example().printFunType()
    Example().printFunType(999)

    var example: Example? = null
    println(example.toString())
    example = Example()
    println(example.toString())
    val list = mutableListOf(1, 2, 3)
    println(list.lastIndex)

    MyClass.printCompanion()

    Connection(Host("jinyang.cn"), 666).connect()

    BaseCaller().call(Base())
    BaseCaller().call(Derived())
    DerivedCaller().call(Base())//分发接收者虚拟解析
    DerivedCaller().call(Derived())//扩展接收者静态解析

    User("", 0)
    User2()

    val p = People2("bob")
    p.age = 11
    val p2 = People2("bob")
    p2.age = 22
    println(p.toString())
    println(p.equals(p2))

    val u1 = User2("tomi", 11)
    val u2 = u1.copy("tomi_copy")
    println(u1.toString())
    println(u2.toString())

    //数据类与解构声明
    //为数据类⽣成的  Component 函数 使它们可在解构声明中使⽤
    val (name, age) = u1
    println("$name, $age years old")

    //使⽤密封类的关键好处在于使⽤ when 表达式 的时候，如果能够验证语句覆盖了所有情况，就不需要为该语句再添加
    //⼀个 else ⼦句了。当然，这只有当你⽤ when 作为表达式（使⽤结果）⽽不是作为语句时才有⽤
    fun eval(exor: Exor): Double = when (exor) {
        is Const -> exor.number
        is Sum -> eval(exor.e1) + eval(exor.e2)
        NotANumber -> Double.NaN
        // 不再需要 `else` ⼦句，因为我们已经覆盖了所有的情况
    }

    println(eval(Const(10.0)))
    println(eval(Sum(Const(10.0), Const(9.0))))
    println(eval(NotANumber))
    println(eval(Sum(Const(10.0), NotANumber)))

//    Elvis 操作符
    var person: Person? = null
    val a = person?.name ?: "name=null"
    println("Elvis:a1=" + a)
    person = Person(null, null)
    val a2 = person?.name ?: "name=null"
    println("Elvis:a2=" + a2)
    person.name = ""
    val a3 = person?.name ?: "name=null"
    println("Elvis:a3=" + a3)
    var person2: Person? = null
    val a4 = person2?.email?.length ?: "person=null|email=null"
    println("Elvis:a4=" + a4)
    person2 = Person(null, null)
    val a5 = person2?.email?.length ?: "person=null|email=null"
    println("Elvis:a5=" + a5)
    person2.email = ""
    val a6 = person2?.email?.length ?: "person=null|email=null"
    println("Elvis:email.len=" + a6)

    //类型转换
    val y = 100
    val z: String? = y as? String ?: "not string"
    println(z)
    val x = null
    println(if (x is String) x else "not string")

    var m: String? = null
//    print(m.length)
    println(m?.length)
    var n: String = ""
//    n=m //编译报错，String? 类型不可以赋值给 String 类型
    m = n

//    相等性
//    var d=Person("bob","")
//    var e=Person("bob","")
//    var d="bob"
//    var e="bob"
    var d: String? = null
    var e: String? = null
    var f = d
    //结构相等:
    println(d == e)
    println(d == f)
    println(d?.equals(e) ?: (e === null))
    println(d?.equals(f) ?: (f === null))
    println(d?.equals(e))
    println(d?.equals(f))
    //引用相等
    println(d === e)
    println(d === f)

    // 定义一个数据类，其中成员变量 name 是不可为空的 String 类型，默认值是 MOMO
    data class Person(val age: Int, val name: String = "Kotlin")
//    val per = Gson().fromJson("""{"age":42}""", Person::class.java)
//    println(per.toString()) // 输出 null
    //由于 Gson 最初是为 Java 语言设计的序列化框架，并不支持 Kotlin 不可为空、默认值这些特性，
    // 从而导致原本不可为空的属性变成null，原本应该有默认值的变量没有默认值。
    val moshi = Moshi.Builder().build()
    val adapter = moshi.adapter(Person::class.java)
    val per2 = adapter.fromJson("""{"age":42}""")
    println(per2.toString())

    //委托
    val cat = CatA()
    Zoo(cat).bark()

    //懒加载
    println(lazyValue)
    println("+++++")
    println(lazyValue)

    //订阅者模式
    val user = UserA()
    user.name = "first.bob"
    user.name = "second.bob"

    //枚举常量
    printAllValues<RGB>()

    C1().bar()

    useData()

    println(pwd)
    pwd.logInfo()

    println(pwd.print())

    useCompanion()

    testInterf()

    testBy()

    testDelegate()

    testLazy()

    testObservable()

    testMap1()
}




